/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.logging;

import java.lang.reflect.Constructor;

/**
 * @author Clinton Begin
 * @author Eduardo Macarron
 */
/**
 * 日志工厂
 * 
 */
public final class LogFactory {
	/**
	 * Marker to be used by logging implementations that support markers(支持标记的日志实现中使用的标记)
	 * 给支持marker功能的logger使用(目前有slf4j, log4j2)
	 */
	public static final String MARKER = "MYBATIS";

	/**
	 * 具体究竟用哪个日志框架，那个框架所对应logger的构造函数
	 */ 
	private static Constructor<? extends Log> logConstructor;
	// 按照顺序依次去尝试着实例化各个实例
	static {
		// 这边乍一看以为开了几个并行的线程去决定使用哪个具体框架的logging，其实不然
		// slf4j
		tryImplementation(new Runnable() {
			@Override
			public void run() {
				useSlf4jLogging();
			}
		});
		// common logging
		tryImplementation(new Runnable() {
			@Override
			public void run() {
				useCommonsLogging();
			}
		});
		// log4j2
		tryImplementation(new Runnable() {
			@Override
			public void run() {
				useLog4J2Logging();
			}
		});
		// log4j
		tryImplementation(new Runnable() {
			@Override
			public void run() {
				useLog4JLogging();
			}
		});
		// jdk logging
		tryImplementation(new Runnable() {
			@Override
			public void run() {
				useJdkLogging();
			}
		});
		// 没有日志
		tryImplementation(new Runnable() {
			@Override
			public void run() {
				useNoLogging();
			}
		});
	}

	// 单例模式，不能自己new实例
	private LogFactory() {
		// disable construction
	}

	/**
	 * 根据传入的类来构建Log
	 * 
	 * @param aClass
	 * @return
	 */
	public static Log getLog(Class<?> aClass) {
		return getLog(aClass.getName());
	}

	/**
	 * 根据传入的类名来构建Log
	 * 
	 * @param logger
	 * @return
	 */
	public static Log getLog(String logger) {
		try {
			// 构造函数，参数必须是一个，为String型，指明logger的名称
			return logConstructor.newInstance(new Object[] { logger });
		} catch (Throwable t) {
			throw new LogException("Error creating logger for logger " + logger + ".  Cause: " + t, t);
		}
	}

	/**
	 * 提供一个扩展功能，如果以上log都不满意，可以使用自定义的log
	 * 
	 * @param clazz
	 */
	public static synchronized void useCustomLogging(Class<? extends Log> clazz) {
		setImplementation(clazz);
	}

	public static synchronized void useSlf4jLogging() {
		setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
	}

	public static synchronized void useCommonsLogging() {
		setImplementation(org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl.class);
	}

	public static synchronized void useLog4JLogging() {
		setImplementation(org.apache.ibatis.logging.log4j.Log4jImpl.class);
	}

	public static synchronized void useLog4J2Logging() {
		setImplementation(org.apache.ibatis.logging.log4j2.Log4j2Impl.class);
	}

	public static synchronized void useJdkLogging() {
		setImplementation(org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl.class);
	}

	// 这个没用到
	public static synchronized void useStdOutLogging() {
		setImplementation(org.apache.ibatis.logging.stdout.StdOutImpl.class);
	}

	public static synchronized void useNoLogging() {
		setImplementation(org.apache.ibatis.logging.nologging.NoLoggingImpl.class);
	}
	/**
	 * 尝试实施
	 * @param runnable
	 */
	private static void tryImplementation(Runnable runnable) {
		if (logConstructor == null) {
			try {
				// 这里调用的不是start,而是run！根本就没用多线程嘛！
				runnable.run();
			} catch (Throwable t) {
				// ignore
			}
		}
	}
	/**
	 * 设置实施方案
	 * @param implClass
	 */
	private static void setImplementation(Class<? extends Log> implClass) {
		try {
			// 得到构造方法
			Constructor<? extends Log> candidate = implClass.getConstructor(new Class[] { String.class });
			// 创建日志实例
			Log log = candidate.newInstance(new Object[] { LogFactory.class.getName() });
			log.debug("Logging initialized using '" + implClass + "' adapter.");
			// 设置logConstructor,一旦设上，表明找到相应的log的jar包了，那后面别的log就不找了。
			logConstructor = candidate;
		} catch (Throwable t) {
			throw new LogException("Error setting Log implementation.  Cause: " + t, t);
		}
	}

}
